﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

/*
* the header of the TSDirEntry is organized as follows:
* +-------------+------------+-----------+
* | Separator   | DirEntry   | Separator |
* +-------------+------------+-----------+
*/

namespace TSTree
{
    public class TSDirEntry
    {
        public TRTree myTree;                      // pointer to my TS-tree
        public int son;                            // block # of son
        public TSNode sonPtr;              		// pointer to son if in main mem.
        public bool sonIsData;                // TRUE, if son is a data page
        public int dimension;                      // dimension of the box
        public float[] bounces;                    // the mbr of the box
        public int sonLevel;                      // level of the node pointed to
        public int noOfData;                    // amount of data entries behind the
        // son of this entry    
        public float[] leftsep;                    // seperators
        public float[] rightsep;                   // seperators


        public TSDirEntry(int _dimension, bool _sonIsData, TRTree rt)
        {
            dimension = _dimension;
            sonIsData = _sonIsData;
            myTree = rt;
            bounces = new float[2 * dimension];
            leftsep = new float[dimension];
            rightsep = new float[dimension];


            sonPtr = null;
            noOfData = 0;
           
        }
        /**
        * copy the contents of this to another Direntry object
        */
        public void copyTo(TSDirEntry target)
        {
            target.dimension = dimension;
            target.son = son;
            target.sonPtr = sonPtr;
            target.sonLevel = sonLevel;
            target.sonIsData = sonIsData;
            target.noOfData = noOfData;
     
            for (int i = 0; i < 2 * dimension; i++)
            {
                target.bounces[i] = bounces[i];
            }
            for (int j = 0; j < dimension; j++)
            {
                target.leftsep[j] = leftsep[j];
                target.rightsep[j] = rightsep[j];
            }
        }

        /**
        * Checks if point v is inside the entry's MBR
        */
        public bool isInside(float[] v)
        {
            int i;

            for (i = 0; i < dimension; i++)
            {
                if (v[i] < bounces[2 * i] || v[i] > bounces[2 * i + 1])
                    return false;
            }
            return true;
        }

      public bool IsBigger(float[] l1, float[] l2)
        {
            // |a| ≤ |b| ∧ ∀i ∈ {1, . . . , |a|} : ai = bi 
            if (l1.Length <= l2.Length)
            {
                bool flag = true;
                for (int i = 0; i < l1.Length; i++)
                {
                    if (l1[i] != l2[i]) flag = false;
                }
                if (flag) return true;
            }
            //∃j ≤ min{|a|, |b|} ∀i ∈ {1, . . . , j − 1} : ai = bi ∧ aj > bj 
            int n = Math.Min(l1.Length, l2.Length);
            bool flag2 = true;
            for (int i = 0; i < n - 1; i++)
            {
                if (l1[i] != l2[i]) { flag2 = false; break; }
            }
            if (flag2 == true && l1[n - 1] > l2[n - 1]) return true;

            return false;
        }
       public bool IsSmaller(float[] l1, float[] l2)
        {
            // |a| ≤ |b| ∧ ∀i ∈ {1, . . . , |a|} : ai = bi 
            if (l1.Length <= l2.Length)
            {
                bool flag = true;
                for (int i = 0; i < l1.Length; i++)
                {
                    if (l1[i] != l2[i]) { flag = false; break; }
                }
                if (flag == true) return true;
            }
            //∃j ≤ min{|a|, |b|} ∀i ∈ {1, . . . , j − 1} : ai = bi ∧ aj < bj 
            int n = Math.Min(l1.Length, l2.Length);
            bool flag2 = true;
            for (int i = 0; i < n - 1 ; i++)
            {
                if (l1[i] != l2[i]) { flag2 = false; break; }
            }
            if (flag2 == true && l1[n - 1] < l2[n - 1]) return true;

            return false;
        }
       public float leftMinDist(int startIndex, float[] q, float[] leftsep , float[] rightsep)
        {
            float dist1, dist2 = 0;

            if (startIndex == q.Length - 1)
            {
                dist1 = Math.Abs(q[startIndex] - leftsep[startIndex]);
                return dist1;
            }
            else
            {
                dist1 = Math.Abs(q[startIndex] - leftsep[startIndex]) + leftMinDist(startIndex + 1, q,leftsep,rightsep);
            }

            if (!(leftsep[startIndex] + 1 < rightsep[startIndex]
                || (leftsep[startIndex] + 1 <= rightsep[startIndex]
                     && leftsep[startIndex + 1] <= rightsep[startIndex + 1])
                  )
               )
            {
                
                dist2 = Math.Abs(q[startIndex] - (leftsep[startIndex] + 1)) + Utilities.DTWdistance(dimension, startIndex, q, this.bounces);
            }
           
            return Math.Min(dist1, dist2);
        }

      public float rightMinDist(int startIndex, float[] q, float[] leftsep, float[] rightsep)
       {
           float dist1, dist2 = 0;

           if (startIndex == q.Length - 1)
           {
               dist1 = Math.Abs(q[startIndex] - rightsep[startIndex]);
               return dist1;
           }
           else
           {
               dist1 = Math.Abs(q[startIndex] - rightsep[startIndex]) + rightMinDist(startIndex + 1, q, leftsep, rightsep);
           }

           if (!(rightsep[startIndex] + 1 < leftsep[startIndex]
               || (rightsep[startIndex] + 1 <= leftsep[startIndex]
                    && rightsep[startIndex + 1] <= leftsep[startIndex + 1])
                 )
              )
           {
              
               dist2 = Math.Abs(q[startIndex] - (rightsep[startIndex] - 1 )) + Utilities.DTWdistance(dimension, startIndex, q, this.bounces);
           }

           return Math.Min(dist1, dist2);
       }
      

        public float minDist(float[] point)
        {
            float md = 0;
            if (point.Length == 0)
            {
                for (int i = 0; i < dimension * 2 ; i+=2)
                {
                    md += (float)Math.Sqrt(point[i / 2] > this.bounces[i + 1] ? Math.Pow(point[i / 2] - this.bounces[i + 1], 2) : (point[i / 2] < this.bounces[i] ? Math.Pow(point[i / 2] - this.bounces[i], 2) : 0));
                }
            }
            else
            {
                if (IsSmaller(point, this.leftsep))
                {
                    md += leftMinDist(0, point, this.leftsep, this.rightsep);
                }
                else if (IsBigger(point, this.rightsep))
                {
                    md += rightMinDist(0, point, this.leftsep, this.rightsep);
                }
                else{
                    md +=0;
                }
              
            }
         
            return md;
        }
        /**
         * Tests if the parameter mbr is inside or overlaps the MBR of the entry
         * Constants.INSIDE :inside
         * Constants.OVERLAP: overlap
         * Constants.S_NONE: None
        */
        public int interSection(float[] mbr)
        {
            bool inside;
            bool overlap;
            int i;

            overlap = true;
            inside = true;
            for (i = 0; i < dimension; i++)
            {
                if (mbr[2 * i] > bounces[2 * i + 1] || mbr[2 * i + 1] < bounces[2 * i])
                    overlap = false;
                if (mbr[2 * i] < bounces[2 * i] || mbr[2 * i + 1] > bounces[2 * i + 1])
                    inside = false;
            }
            if (inside)
                return Constants.INSIDE;
            else if (overlap)
                return Constants.OVERLAP;
            else
                return Constants.S_NONE;
        }
        /**
       * reads from the input stream the object's info
       * used by TSDirNode.readFromBuffer()
       */
        public void readFromBuffer(BinaryReader din)
        {
            for (int i = 0; i < 2 * dimension; ++i)
                bounces[i] = din.ReadSingle();
            son = din.ReadInt32();
            noOfData = din.ReadInt32();
        }
        /**
    * writes to the output stream the object's info
    * used by TSDirNode.write_to_buffer()
    */
        public void writeToBuffer(BinaryWriter dout)
        {
            for (int i = 0; i < 2 * dimension; ++i)
                dout.Write(bounces[i]);
            dout.Write(son);
            dout.Write(noOfData);
        }
        /************************************************************************/
        /* Get the size in number of bytes of a DirEntry                                                                      */
        /************************************************************************/
        public int getSize()
        {
            return 2 * dimension * Constants.SIZEOF_FLOAT + Constants.SIZEOF_INT
                            + Constants.SIZEOF_INT;
        }

        public int getleftSepLengh()
        {
            for (int i = 0; i < dimension; i++)
            {
                if (leftsep[i] == null) return i;
            }
            return dimension;
        }
        public int getrightSepLengh()
        {
            for (int i = 0; i < dimension; i++)
            {
                if (rightsep[i] == null) return i;
            }
            return dimension;
        }
        /**
        * returns the son_ptr (the node this entry points to
        * if the node is not in main memory, it it read
        * from disk (see TSDirNode/RTDataNode constructor)
        */
        public TSNode getSon()
        {
            if (sonPtr == null)
            {
                if (sonIsData)
                    sonPtr = new TSDataNode(myTree, son);
                else
                    sonPtr = new TSDirNode(myTree, son);
            }
            return sonPtr;
        }
        /**
        * returns true if the entry intersects the circle
        */
        public  bool isInterSecCircle(TSPoint center, float radius)
        {
            return Utilities.isCircleInterSection(dimension, bounces, center, radius);
        }
        /**
   * returns true if the entry intersects the ring
   */
        public bool isRingInterSection(TSPoint center, float radius1, float radius2)
        {
            return Utilities.isRingInterSection(dimension, bounces, center, radius1, radius2);
        }
        public void delete()
        {
            if (sonPtr != null)
                ((INode)sonPtr).delete();
        }

    }
}
